#include "maindb.h"
#include "mainconfig.h"
#include "mainctrl.h"

int CMainDB::Init(CMainCtrl& stOwern, CTEpoll& stEpoll)
{
    int iRet = 0;
    m_pstOwner = &stOwern;
    m_pstEpoll = &stEpoll;

    iRet = m_stRecvBuffer.Attach(NULL, 65536 * 100, 0);
    CHECK_RETURN(iRet);

    iRet = m_stSendBuffer.Attach(NULL, 65536 * 100, 0);
    CHECK_RETURN(iRet);

    // 创建socket
    iRet = m_stSocket.Create();
    CHECK_RETURN(iRet);

    // 注册epoll
    iRet = Register(*this, &CMainDB::OnDBEvent, stEpoll,
        m_stSocket.GetSocket(), EPOLLOUT); // 连接时是监听OUT事件
    CHECK_RETURN(iRet);

    // 连接dbsvr
    CheckConnect();

    return 0;
}

void CMainDB::OnDBEvent(CTEpollObject<CMainDB>* pstObject, SOCKET iSocket, int iEvent)
{
    int iRet = 0;
    CT_TRACE(("recv db event %d, socket = %d, connect stat is %d", iEvent, iSocket, m_iConnectState));

    // 连接成功
    if (CTEpoll::IsOutputEvent(iEvent))
    {
        CT_TRACE(("db connect(%s:%hu) ok, socket=%d", CONF->DBHost(), CONF->DBPort, iSocket));
        m_iConnectState = DB_CONNECTED;
        m_OldConnectFlag = DB_CONNECTED;
        m_ConnectCountFlag = ++m_ConnectCountFlag % 100000000;
        CT_TRACE(("Conncet dbserver Flag=%d", m_ConnectCountFlag));
        // 改为监听IN事件
        iRet = ModRegEvent(EPOLLIN);
        if (iRet)
        {
            CT_ERROR(("ModRegEvent failed %d, errno=%d, socket=%d", iRet, CT_ERRNO, iSocket));
            return;
        }

        // 断线重连后需要发送残留的请求
        CheckSend();
    }

    // 有数据返回(对端断开连接也会出发该事件)
    if (CTEpoll::IsInputEvent(iEvent))
    {
        Recv();
    }

    // 连接被断开
    if (CTEpoll::IsCloseEvent(iEvent))
    {
        CT_WARNING(("db connect(%s:%hu) close, socket=%d, event = %d", CONF->DBHost(), CONF->DBPort, iSocket, iEvent));
        //m_iConnectState = DB_CLOSED;
        ReConnect();
    }
}

void CMainDB::CheckConnect()
{
    if (m_iConnectState == DB_CLOSED)
    {
        int iRet = 0;
        iRet = m_stSocket.Connect(CONF->DBHost(), CONF->DBPort);
        if (iRet)
        {
            CT_WARNING(("db connect(%s:%hu) failed, ret=%d", CONF->DBHost(), CONF->DBPort, iRet));
            return;
        }

        CT_TRACE(("connect(%s:%hu) ok, socket = %d", CONF->DBHost(), CONF->DBPort, m_stSocket.GetSocket()));
        m_iConnectState = DB_CONNECTING;
    }
}


void CMainDB::Recv()
{
	CT_TRACE(("recv begin"));

    do
    {
        char* pszMsgBuf = NULL;
        int iMsgLen = 0;
        int iRet = RecvOneCode(m_stSocket, m_stRecvBuffer, pszMsgBuf, iMsgLen);
        if (iRet)
        {
            if ((m_OldConnectFlag != m_iConnectState))
            {
                CT_TRACE(("disconnection dbserver ret = %d Flag=%d state=%d", iRet, m_ConnectCountFlag, m_iConnectState));
                m_OldConnectFlag = m_iConnectState;
            }
            return;
        }

        if (iMsgLen > 0)
        {
            m_pstOwner->HandleDBMsg(pszMsgBuf, iMsgLen);
            m_stRecvBuffer.Remove(iMsgLen + sizeof(unsigned int));

        }
    } while(m_stRecvBuffer.GetUsedLen());
    CT_TRACE(("recv end"));
}

int CMainDB::Send(const char* pszBuf, int iBufLen)
{
    int iRet = m_stSendBuffer.Append(pszBuf, iBufLen);
    if (iRet)
    {
        CT_ERROR(("store %d bytes in send buffer failed, ret=%d, buffer freelen=%d",
            iBufLen, iRet, m_stSendBuffer.GetFreeLen()));
    }
    CheckSend();
    return iRet;

    /*
    int iRet = 0;
    if (m_iConnectState != DB_CONNECTED)
    {
        // 还没连接, 此时尽量缓存
        CT_WARNING(("db's connect state is %d, not connected, store %d bytes",
            m_iConnectState, iBufLen));

        iRet = m_stSendBuffer.Append(pszBuf, iBufLen);
        if (iRet)
        {
            CT_WARNING(("store %d bytes failed, ret=%d, buffer freelen=%d",
                iBufLen, iRet, m_stSendBuffer.GetFreeLen()));
        }

        return iRet;
    }

    int iSendLen = 0;
    iRet = m_stSocket.SendEx(pszBuf, iBufLen, iSendLen);
    if (iRet && iRet != CT_EINPROGRESS && iRet != CT_EWOULDBLOCK)
    {
        CT_WARNING(("socket recv error, ret %d", iRet));
        ReConnect();
        CT_WARNING(("SendEx failed, ret=%d", iRet));
        return iRet;
    }

    if (iSendLen == iBufLen)
    {
        return 0;
    }
    else if (iSendLen < iBufLen)
    {
        // 没发送完, 保存在缓冲区内
        m_stSendBuffer.Append(pszBuf + iSendLen, iBufLen - iSendLen);
        return 0;
    }
    else
    {
        CT_WARNING(("SendEx invalid. %d > %d", iSendLen, iBufLen));
        return -1;
    }
    */
}

void CMainDB::CheckSend()
{
    if (m_stSendBuffer.GetUsedLen() == 0)
    {
        return;
    }

    if (DB_CONNECTED != m_iConnectState)
    {
        CT_WARNING(("db not connect, now stat is %d, check send return", m_iConnectState));
        return;
    }

    int iRet = 0;
    int iSendLen = 0;
    iRet = m_stSocket.SendEx(m_stSendBuffer.GetUsedBuf(),
        m_stSendBuffer.GetUsedLen(), iSendLen);
    if (iRet && iRet != CT_EINPROGRESS && iRet != CT_EWOULDBLOCK)
    {
        CT_WARNING(("socket send error, ret %d", iRet));
        ReConnect();
        return;
    }

    CT_TRACE(("db need send %d bytes, actually send %d bytes",
        m_stSendBuffer.GetUsedLen(), iSendLen));

    m_stSendBuffer.Remove(iSendLen);
}
/*
接收数据，此处逻辑分块并不太好，
建议将物理数据的接收和逻辑数据的接收分开
*/
int CMainDB::RecvOneCode(CTSocket& stSocket, CTBuffer& stBuffer, char*& pszCodeBuf, int& iCodeLen)
{
    pszCodeBuf = NULL;
    iCodeLen = 0;

    int iRet = 0;
    int iRecvLen = 0;
/*
    if (stBuffer.GetUsedLen() > 0)
    {
        CT_WARNING(("RecvOneCode used buffer has %d bytes", stBuffer.GetUsedLen()));
    }
*/
    //stBuffer.GetFreeLen() - 1 预留一个字节, 存放'\0'
    iRet = stSocket.Recv(stBuffer.GetFreeBuf() , stBuffer.GetFreeLen() - 1, iRecvLen);
    if (iRet)
    {
        //获取信息失败连接已经断开
        CT_WARNING(("socket recv error, ret %d", iRet));
        ReConnect();
        return iRet;
    }

    stBuffer.Append(iRecvLen);
    unsigned int iMsgLen = ntohl(*(unsigned int*)stBuffer.GetUsedBuf());

    if (iMsgLen == 0)
    {
        pszCodeBuf = NULL;
        iCodeLen = -1;
        return 0;
    }

    // 注意这里移动后, 字符串协议就不是以\0结尾了
    //stBuffer.Remove(sizeof(iMsgLen));

    // 在最后加个'\0'
    // 对二进制协议没有用, 对字符串协议来说, 需要有\0结尾来
    //*(char*)(stBuffer.GetUsedBuf() + stBuffer.GetUsedLen()) = '\0';

    if (iMsgLen > (unsigned int)stBuffer.GetUsedLen()) // 消息没收完
    {
        pszCodeBuf = NULL;
        iCodeLen = 0;
    }
    else if (iMsgLen == (unsigned int)stBuffer.GetUsedLen())
    {
        pszCodeBuf = stBuffer.GetUsedBuf() + sizeof(unsigned int);
        iCodeLen = iMsgLen - sizeof(unsigned int);
    }
    else
    {
        pszCodeBuf = stBuffer.GetUsedBuf() + sizeof(unsigned int);
        iCodeLen = iMsgLen - sizeof(unsigned int);

        // -- 有可能一次收到多个返回包 --
        // 还有预留的数据, 不太合理
        //CT_WARNING(("remain data. stBuffer.GetUsedLen()=%d iMsgLen=%d",
        //    stBuffer.GetUsedLen(), iMsgLen));
    }

    return 0;
}

void CMainDB::ReConnect()
{
    m_iConnectState = DB_CLOSED;

    //重新注册
    Reregister(m_stSocket);

    //重连
    CheckConnect();
}

